//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// C# Port port by: Lars Brubaker
//                  larsbrubaker@gmail.com
// Copyright (C) 2007
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// classes slider_ctrl_impl, slider_ctrl
//
//----------------------------------------------------------------------------
using System;
using AGG.VertexSource;

namespace AGG.UI
{
    //--------------------------------------------------------slider_ctrl_impl
    public class SliderWidget : SimpleVertexSourceWidget
    {
        public delegate void ValueChangedEventHandler(object sender, EventArgs e);
        public event ValueChangedEventHandler ValueChanged;

        double m_border_width;
        double   m_border_extra;
        double   m_text_thickness;
        double   m_value;
        double   m_preview_value;
        double   m_min;
        double   m_max;
        int m_num_steps;
        bool     m_descending;
        string   m_label = "";
        double   left;
        double   bottom;
        double   right;
        double   top;
        double   m_pdx;
        protected bool     m_mouse_move;
        private RGBA_Doubles m_background_color;
        private RGBA_Doubles m_triangle_color;
        private RGBA_Doubles m_text_color;
        private RGBA_Doubles m_pointer_preview_color;
        private RGBA_Doubles m_pointer_color;

        double[] m_vx = new double[32];
        double[] m_vy = new double[32];

        VertexSource.Ellipse  m_ellipse;

        int m_idx;
        int m_vertex;

        gsv_text              m_text;
        conv_stroke           m_text_poly;
        PathStorage          m_storage;

        rect_d graphicsBounds;

        public SliderWidget(double x1, double y1, double x2, double y2)
            : base(x1, y1, x2, y2)
		{
            graphicsBounds = Bounds;
            rect_d newBounds = Bounds;
            newBounds.Inflate(5);
            Bounds = newBounds;
			m_border_width = 1;
            m_border_extra = graphicsBounds.Height / 2;
			m_text_thickness = 1;
			m_pdx = 0;
			m_mouse_move = false;
			m_value = (0.5);
			m_preview_value = (0.5);
			m_min = (0.0);
			m_max = (1.0);
			m_num_steps = (0);
			m_descending = (false);
            m_ellipse = new VertexSource.Ellipse();
            m_storage = new PathStorage();
            m_text = new gsv_text();
            m_text_poly = new conv_stroke(m_text);

			calc_box();

            m_background_color = (new RGBA_Doubles(1.0, 0.9, 0.8));
            m_triangle_color = (new RGBA_Doubles(0.7, 0.6, 0.6));
            m_text_color = (new RGBA_Doubles(0.0, 0.0, 0.0));
            m_pointer_preview_color = (new RGBA_Doubles(0.6, 0.4, 0.4, 0.4));
            m_pointer_color = (new RGBA_Doubles(0.8, 0.0, 0.0, 0.6));
        }

        public void range(double min, double max) { m_min = min; m_max = max; }
        public void num_steps(int num) { m_num_steps = num; }
		public void label(String fmt)
		{
            m_label = fmt;
		}
        public void text_thickness(double t) { m_text_thickness = t; }

        public bool descending() { return m_descending; }
        public void descending(bool v) { m_descending = v; }

        public double value() { return m_value * (m_max - m_min) + m_min; }
		public void value(double value) 
		{ 
			m_preview_value = (value - m_min) / (m_max - m_min); 
			if(m_preview_value > 1.0) m_preview_value = 1.0;
			if(m_preview_value < 0.0) m_preview_value = 0.0;
			normalize_value(true);
		}

		public override bool InRect(double x, double y)
		{
            Transform.inverse_transform(ref x, ref y);
            return graphicsBounds.hit_test(x, y);
		}

        private bool IsOver(double x, double y)
        {
            double xp = left + (right - left) * m_value;
            double yp = (bottom + top) / 2.0;
            return (agg_math.calc_distance(x, y, xp, yp) <= graphicsBounds.Top - graphicsBounds.Bottom);
        }

        public override void OnMouseDown(MouseEventArgs mouseEvent)
		{
            double x = mouseEvent.X;
            double y = mouseEvent.Y;
            Transform.inverse_transform(ref x, ref y);

			double xp = left + (right - left) * m_value;
			double yp = (bottom + top) / 2.0;

            if (IsOver(x, y))
			{
				m_pdx = xp - x;
				m_mouse_move = true;
			}
		}

        public override void OnMouseUp(MouseEventArgs mouseEvent)
		{
        	if(m_mouse_move)
        	{
				m_mouse_move = false;
				normalize_value(true);
        	}
		}

        public override void OnMouseMove(MouseEventArgs mouseEvent)
		{
            double x = mouseEvent.X;
            double y = mouseEvent.Y;
            Transform.inverse_transform(ref x, ref y);

            if (m_mouse_move)
            {
                double xp = x + m_pdx;
                m_preview_value = (xp - left) / (right - left);
                if (m_preview_value < 0.0) m_preview_value = 0.0;
                if (m_preview_value > 1.0) m_preview_value = 1.0;

                if (ValueChanged != null)
                {
                    ValueChanged(this, mouseEvent);
                }

                Invalidate();
            }
		}

        public override void OnKeyDown(KeyEventArgs keyEvent)
		{
			double d = 0.005;
			if(m_num_steps != 0)
			{
				d = 1.0 / m_num_steps;
			}

			if(keyEvent.KeyCode == Keys.Right 
                || keyEvent.KeyCode == Keys.Up)
			{
				m_preview_value += d;
				if(m_preview_value > 1.0) m_preview_value = 1.0;
				normalize_value(true);
                keyEvent.Handled = true;
			}

            if (keyEvent.KeyCode == Keys.Left 
                || keyEvent.KeyCode == Keys.Down)
            {
				m_preview_value -= d;
				if(m_preview_value < 0.0) m_preview_value = 0.0;
				normalize_value(true);
                keyEvent.Handled = true;
            }
		}

        // Vertex source interface
        public override int num_paths() { return 6; }

        public override void rewind(int idx)
		{
			m_idx = idx;

			switch(idx)
			{
			default:

			case 0:                 // Background
				m_vertex = 0;
                m_vx[0] = graphicsBounds.Left - m_border_extra;
                m_vy[0] = graphicsBounds.Bottom - m_border_extra;
                m_vx[1] = graphicsBounds.Right + m_border_extra;
                m_vy[1] = graphicsBounds.Bottom - m_border_extra;
                m_vx[2] = graphicsBounds.Right + m_border_extra;
                m_vy[2] = graphicsBounds.Top + m_border_extra;
                m_vx[3] = graphicsBounds.Left - m_border_extra;
                m_vy[3] = graphicsBounds.Top + m_border_extra;
				break;

			case 1:                 // Triangle
				m_vertex = 0;
				if(m_descending)
				{
                    m_vx[0] = graphicsBounds.Left;
                    m_vy[0] = graphicsBounds.Bottom;
                    m_vx[1] = graphicsBounds.Right;
                    m_vy[1] = graphicsBounds.Bottom;
                    m_vx[2] = graphicsBounds.Left;
                    m_vy[2] = graphicsBounds.Top;
                    m_vx[3] = graphicsBounds.Left;
                    m_vy[3] = graphicsBounds.Bottom;
				}
				else
				{
                    m_vx[0] = graphicsBounds.Left;
                    m_vy[0] = graphicsBounds.Bottom;
                    m_vx[1] = graphicsBounds.Right;
                    m_vy[1] = graphicsBounds.Bottom;
                    m_vx[2] = graphicsBounds.Right;
                    m_vy[2] = graphicsBounds.Top;
                    m_vx[3] = graphicsBounds.Left;
                    m_vy[3] = graphicsBounds.Bottom;
				}
				break;

			case 2:
				m_text.text(m_label);
				if(m_label.Length > 0)
				{
					string buf;
                    buf = string.Format(m_label, value());
					m_text.text(buf);
				}
                m_text.start_point(graphicsBounds.Left, graphicsBounds.Bottom);
                m_text.SetFontSize((graphicsBounds.Top - graphicsBounds.Bottom) * 1.2);
				m_text_poly.width(m_text_thickness);
				m_text_poly.line_join(math_stroke.line_join_e.round_join);
                m_text_poly.line_cap(math_stroke.line_cap_e.round_cap);
				m_text_poly.rewind(0);
                break;

			case 3:                 // pointer preview
				m_ellipse.init(left + (right - left) * m_preview_value,
					(bottom + top) / 2.0,
                    graphicsBounds.Top - graphicsBounds.Bottom,
                    graphicsBounds.Top - graphicsBounds.Bottom, 
					32);
				break;


			case 4:                 // pointer
				normalize_value(false);
				m_ellipse.init(left + (right - left) * m_value,
					(bottom + top) / 2.0,
                    graphicsBounds.Top - graphicsBounds.Bottom,
                    graphicsBounds.Top - graphicsBounds.Bottom, 
					32);
				m_ellipse.rewind(0);
				break;

			case 5:
				m_storage.remove_all();
				if(m_num_steps != 0)
				{
					int i;
					double d = (right - left) / m_num_steps;
					if(d > 0.004) d = 0.004;
					for(i = 0; i < m_num_steps + 1; i++)
					{
						double x = left + (right - left) * i / m_num_steps;
                        m_storage.move_to(x, graphicsBounds.Bottom);
                        m_storage.line_to(x - d * (graphicsBounds.Right - graphicsBounds.Left), graphicsBounds.Bottom - m_border_extra);
                        m_storage.line_to(x + d * (graphicsBounds.Right - graphicsBounds.Left), graphicsBounds.Bottom - m_border_extra);
					}
				}
                break;
			}
		}

        public override Path.FlagsAndCommand vertex(out double x, out double y)
		{
            x = 0;
            y = 0;
            Path.FlagsAndCommand PathAndFlags = Path.FlagsAndCommand.CommandLineTo;
			switch(m_idx)
			{
			case 0:
                if (m_vertex == 0) PathAndFlags = Path.FlagsAndCommand.CommandMoveTo;
                if (m_vertex >= 4) PathAndFlags = Path.FlagsAndCommand.CommandStop;
				x = m_vx[m_vertex];
				y = m_vy[m_vertex];
				m_vertex++;
				break;

			case 1:
                if (m_vertex == 0) PathAndFlags = Path.FlagsAndCommand.CommandMoveTo;
                if (m_vertex >= 4) PathAndFlags = Path.FlagsAndCommand.CommandStop;
				x = m_vx[m_vertex];
				y = m_vy[m_vertex];
				m_vertex++;
				break;

			case 2:
                PathAndFlags = m_text_poly.vertex(out x, out y);
                //return Path.path_flags_e.path_cmd_stop;
                break;

			case 3:
			case 4:
                PathAndFlags = m_ellipse.vertex(out x, out y);
				break;

			case 5:
                PathAndFlags = m_storage.vertex(out x, out y);
				break;

			default:
                PathAndFlags = Path.FlagsAndCommand.CommandStop;
				break;
			}

            if (!Path.is_stop(PathAndFlags))
			{
                Transform.transform(ref x, ref y);
			}

            return PathAndFlags;
		}

		private void calc_box()
		{
            left = graphicsBounds.Left + m_border_width;
            bottom = graphicsBounds.Bottom + m_border_width;
            right = graphicsBounds.Right - m_border_width;
            top = graphicsBounds.Top - m_border_width;
		}

		private bool normalize_value(bool preview_value_flag)
		{
			bool ret = true;
			if(m_num_steps != 0)
			{
				int step = (int)(m_preview_value * m_num_steps + 0.5);
				ret = m_value != step / (double)(m_num_steps);
				m_value = step / (double)(m_num_steps);
			}
			else
			{
				m_value = m_preview_value;
			}

			if(preview_value_flag)
			{
				m_preview_value = m_value;
			}
			return ret;
		}

        public void background_color(RGBA_Doubles c) { m_background_color = c; }
        public void pointer_color(RGBA_Doubles c) { m_pointer_color = c; }

        public override IColorType color(int i) 
        {
            switch(i)
            {
                case 0:
                    return m_background_color;

                case 1:
                    return m_triangle_color;

                case 2:
                    return m_text_color;

                case 3:
                    return m_pointer_preview_color;

                case 4:
                    return m_pointer_color;

                case 5:
                    return m_text_color;
            }

            return m_background_color;
        }
    };
}
